package org.stwy.comm.base;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.rap.common.entity.BaseEntity;
import com.rap.common.exception.ServiceException;
import com.rap.common.service.BaseServiceImpl;
@Service
@Transactional(rollbackFor=Exception.class)
public abstract class BaseRecordsServiceImpl<T extends BaseRecordsEntity, PK extends Serializable> extends BaseServiceImpl<T, PK> {
	protected static final String ALL_ATTR = "ALL";
	
	@Override
	public T save(T entity) throws ServiceException {
		//添加新记录的时候生成编码
		if(StringUtils.isBlank(entity.getUuid())){
			entity.setTheCode(genTheCode());
			entity.setBeginTs(new Date());
			return super.save(entity);
		}else{
			return update(entity);
		}
	}
	/**
	 * 生成编码
	 * @return
	 */
	protected Long genTheCode() throws ServiceException  {
		Long lastCode = getLastCode();
		if(lastCode==null){
			lastCode=1L;
		}
		Long newCode = lastCode+1;
		return newCode;
	}
	protected abstract Long getLastCode() throws ServiceException;
	@Override
	public Iterable<T> save(Iterable<T> entities) throws ServiceException {
		for(T entity : entities){
			save(entity);
		}
		return entities;
	}
	/**
	 * 修改时走该方法
	 * 如果修改了关键字段则保存历史记录。
	 * 
	 * @param entity
	 * @return
	 * @throws ServiceException
	 */
	public T update(T entity) throws ServiceException {
		try{
			T oldEntity = this.get((PK)entity.getUuid());
			boolean needSaveRecord = needSaveRecord(entity,oldEntity);
			if(needSaveRecord){
				//将旧记录的信息保存到新增的历史记录中，再修改记录；
				return saveHistoryAndUpdate(entity,oldEntity);
			}else{
				return updateRecord(entity, oldEntity);
			}
		}catch(Exception e){
			e.printStackTrace();
			throw new ServiceException("修改失败，类型转换错误");
		}
	}
	/**
	 * 保存历史并更新当前记录
	 * @param updateEntity
	 * @param oldEntity
	 * @return
	 * @throws ServiceException
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	private T saveHistoryAndUpdate(T updateEntity, T oldEntity) throws ServiceException, InstantiationException, IllegalAccessException {
		//将历史信息保存到新建记录中
		copyForHistory(oldEntity);
		
		//更新记录信息
		return updateInfo(updateEntity,oldEntity);
	}
	/**
	 * 更新当前记录信息
	 * @param updateEntity
	 * @param oldEntity
	 * @return
	 * @throws ServiceException
	 */
	protected T updateInfo(T updateEntity, T oldEntity) throws ServiceException {
		Long theCode = oldEntity.getTheCode();
		String creator = oldEntity.getCreator();
		Date createTs = oldEntity.getCreateTs();
		BeanUtils.copyProperties(updateEntity, oldEntity);
		oldEntity.setCreator(creator);
		oldEntity.setCreateTs(createTs);
		oldEntity.setBeginTs(new Date());
		oldEntity.setStatus(1);
		oldEntity.setTheCode(theCode);
		return super.save(oldEntity);
	}
	/**
	 * 保存到历史记录
	 * @param oldEntity
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws ServiceException
	 */
	protected void copyForHistory(T oldEntity) throws InstantiationException, IllegalAccessException, ServiceException {
		T historyEntity = (T) oldEntity.getClass().newInstance();
		BeanUtils.copyProperties(oldEntity, historyEntity);
		historyEntity.setStatus(0);
		historyEntity.setUuid(null);
		super.save(historyEntity);
	}
	/**
	 * 更新原有记录
	 * @param updateEntity
	 * @param oldEntity
	 * @return
	 * @throws ServiceException
	 */
	private T updateRecord(T updateEntity, T oldEntity) throws ServiceException {
		String uuid = oldEntity.getUuid();
		Long theCode = oldEntity.getTheCode();
		String creator = oldEntity.getCreator();
		Date createTs = oldEntity.getCreateTs();
		Date beginTs = oldEntity.getBeginTs();
		BeanUtils.copyProperties(updateEntity, oldEntity);
		oldEntity.setUuid(uuid);
		oldEntity.setTheCode(theCode);
		oldEntity.setCreator(creator);
		oldEntity.setCreateTs(createTs);
		oldEntity.setBeginTs(beginTs);
		return super.save(oldEntity);
	}

	/**
	 * 判断是否需要保存历史记录
	 * 逻辑：如果修改后的信息中包含关键字段则需要保存历史记录。
	 * @param updateEntity
	 * @param oldEntity
	 * @return
	 * @throws ServiceException
	 */
	protected boolean needSaveRecord(T updateEntity, T oldEntity) throws ServiceException{
		String[] recordAttrs = getRecordAttrs();
		if(recordAttrs==null||recordAttrs.length==0){
			return false;//不需要保存历史记录
		}else if(recordAttrs[0].equalsIgnoreCase("ALL")){
			return true;//任何修改都需要保存历史记录
		}
		//修改了指定字段才需要保存历史记录
		try {
			for(String recordAttr : recordAttrs){
				  Field fi = updateEntity.getClass().getDeclaredField(recordAttr);
				  fi.setAccessible(true);
				  Object obj1 = fi.get(updateEntity);
				  Object obj2 = fi.get(oldEntity);
				  if(obj1 instanceof BaseEntity&&obj2 instanceof BaseEntity){
					  if(!((BaseEntity) obj1).getUuid().equals(((BaseEntity) obj2).getUuid())){
						  return true;//要检查正确性
					  }
				  }else{
					  if(!obj1.equals(obj2)){
						  return true;//要检查正确性
					  }
				  }
			}
		} catch (Exception e) {
			throw new ServiceException("检查是否需要保存历史记录时出现异常；"+e.getMessage());
		}
		return false;
	}
	/**
	 * 定义修改了哪些字段需要保存历史记录
	 * @return
	 * @throws ServiceException
	 */
	protected abstract String[] getRecordAttrs() throws ServiceException;
	
	/**
	 * 覆盖删除方法，实现逻辑删除
	 * 逻辑删除
	 */
	@Override
	public void delete(PK pk) throws ServiceException {
		T entity = get(pk);
		entity.setStatus(0);
		entity.setValid(0);
		save(entity);
	}
	@Override
	public void delete(T entity) throws ServiceException {
		entity.setStatus(0);
		entity.setValid(0);
		save(entity);
	}
	@Override
	public void delete(PK[] pks) throws ServiceException {
		List<T> entities = new ArrayList<T>();
		for(PK pk : pks){
			T entity = get(pk);
			entity.setStatus(0);
			entity.setValid(0);
			entities.add(entity);
		}
		super.save(entities);
	}
	@Override
	public void delete(Iterable<T> entities) throws ServiceException {
		for(T entity : entities){
			entity.setStatus(0);
			entity.setValid(0);
		}
		super.save(entities);
	}
	public void reuse(String[] pks) throws ServiceException  {
		for(String pk : pks){
			T entity = this.get((PK) pk);
			entity.setValid(1);
			entity.setStatus(1);
			super.save(entity);
		}
	}
	
}
